#pragma once

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <pthread.h>
#include <cstdint>
#include <string>
#include <memory>
#include <sys/socket.h>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "Socket.hpp"
#include "log.hpp"

const std::string wwwroot = "./wwwroot";    // web根目录
const std::string seq = "\r\n";
const std::string homepage = "index.html";
class HttpServer;

class ThreadData
{
public:
    ThreadData(int sockfd,HttpServer *ts):_sockfd(sockfd),http(ts)
    {}
    int _sockfd;
    HttpServer *http;
};

// Http的请求
class HttpRequest
{
public:
    // 反序列化：将Http的请求(请求行，请求报头，请求正文)属性按行分离
    void Deserialize(std::string req)
    {
        while (true)
        {
            size_t pos = req.find(seq);
            if(pos == std::string::npos)
                break;

            std::string temp = req.substr(0,pos);
            if(temp == seq)
                break;
            
            if(temp.empty())
                break;

            req_header.push_back(temp);
            req.erase(0,pos+seq.size());
        }
        text = req;
    }
    void Parse()
    {
        // 解析请求行，并将其分别赋值
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;

        // 确定要访问的目录和文件
        file_path = wwwroot;                        // ./wwwroot
        if(url == "/" || url == "/index.html")
        {
            file_path += "/";
            file_path += homepage;                  // ./wwwroot/index.html
        }
        else
            file_path += url;                       // /a/b/c/d.html->./wwwroot/a/b/c/d.html

        // 确定访问的文件格式
        auto pos = file_path.rfind(".");
        if(pos == std::string::npos)
            suffix = ".html";
        else 
            suffix = file_path.substr(pos);
    }
    // 输出
    void DebugPrint()
    {
        for(auto &line:req_header)
        {
            std::cout << "-------------------------------------" << std::endl;
            std::cout << line << "\n\n";
        }
        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;

        std::cout << text << std::endl;
    }
public:
    std::vector<std::string>req_header;         // 按行分离后的请求
    std::string text;                           // 最后的\r\n
    std::string suffix;                         // 文件格式

    // 解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path;
};

class HttpServer
{
public:
    HttpServer(const uint16_t port):_port(port)
    {
        // 存储文件种类的对应访问方法
        content_types.insert({".html","text/html"});
        content_types.insert({".png","image/png"});
    }
    ~HttpServer()
    {}
    void Start()
    {
        // 建立连接
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        while(true)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(clientip,clientport);
            if(sockfd < 0)
                continue;
            lg(Info, "get a new connect, sock: %d",sockfd);
            
            // 多线程
            pthread_t tid;
            ThreadData* td = new ThreadData(sockfd,this);
            pthread_create(&tid,nullptr,ThreadRun,td);
        }
    }
    // 读取Html文件
    static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        // 以二进制方式读取文件htmlpath
        std::ifstream in(htmlpath,std::ios::binary);
        if(!in.is_open())
            return "";

        // 确定文件大小
        in.seekg(0,std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0,std::ios_base::beg);

        // 读取数据
        std::string content;
        content.resize(len);
        in.read(&content[0],content.size());
        
        // std::string content;
        // std::string line;
        // while (std::getline(in,line))
        // {
        //     content += line;
        // }
        
        in.close();

        return content;
    }
    // 对文件添加后缀名
    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_types.find(suffix);
        // 没找到添加.html
        if(iter == content_types.end())
            return content_types[".html"];
        // 找到了添加该后缀名
        else
            return content_types[suffix];
    }
    void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        memset(buffer,0,sizeof(buffer));

        // 读取从浏览器发到套接字的数据
        // 第一行：请求行
        // 第二行到空行：请求报头
        // 空行到最后：请求正文(可省略)
        ssize_t n = recv(sockfd,buffer,sizeof(buffer),0);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer;

            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();
            // req.DebugPrint();

            // 返回响应的过程
            // 第一行：状态行
            // 第二行到空行：响应报头
            // 空行到最后：响应正文
            std::string text = ReadHtmlContent(req.file_path);;
            bool ok = true;
            // 如果读取到的html内容为空，则读取表示错误的html
            if(text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }

            // 确定其状态码(位于第一行，状态行)
            std::string response_line;
            if(ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";

            // response_line = "HTTP/1.0 302 Found\r\n";                // 重定向：使请求页面跳转到设置人想要跳转的网站

            // 序列化三个响应报头
            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += "\r\n";
            response_header += "Set-Cookie: name=haha&&passwd=12345";
            response_header += "\r\n";

            // response_header += "Location: https://www.qq.com\r\n";
            std::string block_line = "\r\n";

            std::string response = response_line;                   // 第一行：状态行
            response += response_header;                            // 第二行到空行：响应报头
            response += block_line;                                 // 空行
            response += text;                                       // 空行到响应正文

            // std::cout << "-------------------------------------" << std::endl;
            // std::cout << response.c_str() << std::endl;
            // std::cout << "-------------------------------------" << std::endl;

            // 发送给客户端(请求网页)
            send(sockfd,response.c_str(),response.size(),0);
        }

        close(sockfd);
    }
    static void *ThreadRun(void *args)
    {
        // 分离线程，并由其执行后续代码
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        td->http->HandlerHttp(td->_sockfd);

        delete td;
        return nullptr;
    }
private:
    Sock _listensock;
    uint16_t _port;
    std::unordered_map<std::string,std::string>content_types;
};